#pragma once

#include <iostream>
#include <string>
#include <unordered_map>
#include <functional>
#include <cassert>
#include <cstring>
#include <cerrno>
#include "Sock.hpp"
#include "Log.hpp"
#include "Err.hpp"
#include "Epoller.hpp"
#include "Util.hpp"
#include "Protocol.hpp"

namespace Yohifo
{
    static const std::string gip = "127.0.0.1";
    static const uint16_t gport = 8888;
    static const uint16_t gnum = 64;
    class Connection;
    class EpollServer;

    using func_t = function<ns_protocol::Response(ns_protocol::Request &)>;
    using callback_t = function<void(Connection *)>;

    enum
    {
        SIZE = 1024
    };

    class Connection
    {
    public:
        Connection(const int &fd, const uint32_t &events, const std::string &IP, const uint16_t &Port)
            : fd_(fd), events_(events), clientIP_(IP), clientPort_(Port)
        {
        }

        void Register(const callback_t &recv, const callback_t &send, const callback_t &except)
        {
            // 给每一个 fd 都注册对应的方法
            recv_ = recv;
            send_ = send;
            except_ = except;
        }

    public:
        int fd_;
        std::string inbuff_;
        std::string outbuff_;

        // 新增成员
        uint32_t events_;
        std::string clientIP_;
        uint16_t clientPort_;

        // 新增回调函数
        callback_t recv_;
        callback_t send_;
        callback_t except_;

        // 回指指针
        EpollServer *R;
    };

    class EpollServer
    {
    public:
        EpollServer(const func_t &func, uint16_t port = gport)
            : port_(port), func_(func)
        {
        }

        ~EpollServer()
        {
            // // 释放哈希表中的空间
            // for (auto e : connections_)
            //     delete e.second;

            listen_sock_.Close();
            epoller_.Close();
        }

        void InitServer()
        {
            // 初始化TCP服务器老三样
            listen_sock_.Socket();
            listen_sock_.Bind(port_);
            listen_sock_.Listen();
            epoller_.Create(gnum);

            AddConnection(listen_sock_.getFd(), READ | SETET, gip, port_);
        }

        // 事件派发器
        void Dispatcher()
        {
            // int timeout = 1000;
            // int timeout = 0;
            int timeout = -1;

            while (true)
            {
                LoopOnce(timeout);
            }
        }

        void LoopOnce(int timeout)
        {
            int n = epoller_.Wait(revs_, gnum, timeout);
            for (int i = 0; i < n; i++)
            {
                int fd = revs_[i].data.fd;
                uint32_t event = revs_[i].events;

                // 将所有的错误转化为IO错误
                if (event & ERROR || event & ERRHUP)
                    event |= (READ | WRITE);

                // 判断事件类型，需要注意 fd 映射关系不存在的情况
                if ((event & READ) && ConnExists(fd))
                    connections_[fd]->recv_(connections_[fd]);
                if ((event & WRITE) && ConnExists(fd))
                    connections_[fd]->send_(connections_[fd]);
            }
        }

        void AddConnection(const int &fd, const uint32_t &events, const std::string &IP, const uint32_t &Port)
        {
            // 设置 ET 模式
            if (events & SETET)
                Util::SetNoBlock(fd);

            // 添加新的fd及事件进入 epoll
            bool ret = epoller_.AddEvents(fd, READ);
            if (ret == false)
            {
                logMessage(Warning, "epoll_ctr(Add) fail %d %s", errno, strerror(errno));
                close(fd);
            }

            // 注册新的映射关系
            Connection *ct = new Connection(fd, events, IP, Port);
            ct->R = this;

            // 根据fd及事件注册不同的方法
            if (fd == listen_sock_.getFd())
                ct->Register(std::bind(&Yohifo::EpollServer::Accepter, this, std::placeholders::_1), nullptr, nullptr);
            else
                ct->Register(std::bind(&Yohifo::EpollServer::Recver, this, std::placeholders::_1),
                             std::bind(&Yohifo::EpollServer::Sender, this, std::placeholders::_1),
                             std::bind(&Yohifo::EpollServer::Excepter, this, std::placeholders::_1));

            connections_.insert(make_pair(fd, ct));

            logMessage(Debug, "登记了一个新的链接信息 fd:%d IP:%s Port:%d", ct->fd_, ct->clientIP_.c_str(), ct->clientPort_);
        }

        void Accepter(Connection *conn)
        {
            do
            {
                // 获取新的连接
                std::string clientIP;
                uint16_t clientPort;
                int errCode = 0;
                int sock = listen_sock_.Accept(&clientIP, &clientPort, &errCode);
                if (sock > 0)
                {
                    logMessage(Info, "fd:%d IP:%s Port:%d 连接成功", conn->fd_, clientIP.c_str(), clientPort);

                    AddConnection(sock, READ | SETET, clientIP, clientPort);
                }
                else
                {
                    // 根据错误码判断
                    if (errCode == EAGAIN || errCode == EWOULDBLOCK)
                        break;
                    else if (errCode == EINTR)
                        continue;
                    else
                    {
                        logMessage(Warning, "暂时没有获取到新连接 errno:%d strerror:%s", errno, strerror(errno));
                        continue;
                    }
                }

            } while (conn->events_ & SETET);
        }

        void RequestHandler(Connection *conn)
        {
            bool quit = false;

            // 循环式解析，直到将报文处理完毕
            while (!quit)
            {
                // 1、读取完整的报文
                std::string msg;
                int n = ns_protocol::ParsePackage(conn->inbuff_, &msg);
                if (n > 0)
                {
                    // 2、移除报头
                    msg = ns_protocol::RemoveHeader(msg, n);

                    // 3、反序列化
                    ns_protocol::Request req;
                    req.Deserialize(msg);

                    // 4、业务处理
                    ns_protocol::Response res = func_(req);

                    // 5、序列化
                    res.Serialize(&msg);

                    // 6、添加报头
                    msg = ns_protocol::AddHeader(msg);

                    // 7、添加至发送缓冲区
                    conn->outbuff_ = msg;
                }
                else
                    quit = true;
            }
        }

        bool RecvHandler(Connection *conn)
        {
            // 非阻塞式读取
            bool safe = true;
            do
            {
                char buffer[SIZE] = {0};
                ssize_t n = recv(conn->fd_, buffer, sizeof(buffer) - 1, 0);
                if (n > 0)
                {
                    buffer[n] = '\0';
                    conn->inbuff_ += buffer;
                }
                else if (n == 0)
                {
                    // 数据读取完毕了
                    conn->except_(conn);
                    safe = false;
                }
                else
                {
                    if (errno == EAGAIN || errno == EWOULDBLOCK)
                        break;
                    if (errno == EINTR)
                        continue;
                    else
                    {
                        safe = false;
                        conn->except_(conn);
                        break;
                    }
                }
            } while (conn->events_ & SETET);

            return safe;
        }

        void Recver(Connection *conn)
        {
            //如果不安全，就不要继续执行解析操作
            if(!RecvHandler(conn)) return;

            RequestHandler(conn);

            // 如果发送缓冲区有数据，直接先发送
            if (!conn->outbuff_.empty())
                conn->send_(conn);
        }

        bool SendHandler(Connection *conn)
        {
            // 非阻塞式写入
            bool safe = true;
            do
            {
                ssize_t n = send(conn->fd_, conn->outbuff_.c_str(), conn->outbuff_.size(), 0);
                if (n > 0)
                {
                    // 发送后，将发送缓冲区中的数据移出
                    conn->outbuff_.erase(0, n);

                    // 发送缓冲区已经为空了，不再发送
                    if (conn->outbuff_.empty())
                        break;
                }
                else
                {
                    if (errno & EAGAIN || errno & EWOULDBLOCK)
                        break;
                    else if (errno & EINTR)
                        continue;
                    else
                    {
                        safe = false;
                        conn->except_(conn);
                        break;
                    }
                }
            } while (conn->events_ & SETET);

            return safe;
        }

        void Sender(Connection *conn)
        {
            // 同理，出现异常不再执行发送等操作
            if(!SendHandler(conn)) return;

            if (conn->outbuff_.empty()) EnableWriteRead(conn, true, false);
            else EnableWriteRead(conn, true, true);
        }

        void Excepter(Connection *conn)
        {
            int fd = conn->fd_;
            std::string ip = conn->clientIP_;
            uint16_t port = conn->clientPort_;

            // 1、从 epoll 中移除
            epoller_.DelEvents(conn->fd_);

            // 2、从哈希表中解除映射关系
            connections_.erase(conn->fd_);

            // 3、关闭 fd
            close(conn->fd_);

            // 5、释放连接指针
            delete conn;

            logMessage(Debug, "该链接出现异常 fd:%d IP:%s Port:%d", fd, ip.c_str(), port);
        }

        bool ConnExists(int fd)
        {
            return connections_.find(fd) != connections_.end();
        }

        bool EnableWriteRead(Connection *conn, bool read, bool write)
        {
            // 设置对应的事件
            uint32_t events = (read ? READ : 0) | (write ? WRITE : 0) | SETET;

            // 修改事件
            return epoller_.ModEvents(conn->fd_, events);
        }

    private:
        uint16_t port_;
        Sock listen_sock_;
        Epoller epoller_;
        struct epoll_event revs_[gnum];
        func_t func_;
        unordered_map<int, Connection *> connections_; // 为每个已记录的 fd 开辟对应的缓冲区
    };
}